//
// isr.c -- High level interrupt service routines and interrupt request handlers.

#include "common.h"
#include "isr.h"
#include "monitor.h"

isr_t interrupt_handlers[256];

/* Fault interrupts - Details */
static const u8int *faults[] = 
{
	"Division by zero exception", "Debug exception", "Non maskable interrupt",
	"Breakpoint exception", "Into detected overflow", "Out of bounds exception",
	"Invalid opcode exception", "No coprocessor exception", "Double fault",
	"Coprocessor segment overrun", "Bad TSS", "Segment no present",
	"Stack fault", "General protection fault", "Page fault", 
	"Unknown interrupt exception", "Coprocessor fault", 
	"Alignment check exception", "Machine check exception", "Reserved" 
};

void register_interrupt_handler(u8int n, isr_t handler)
{
    interrupt_handlers[n] = handler;
}

// This gets called from our ASM interrupt handler stub.
void isr_handler(registers_t regs)
{
	monitor_write("Received Interrupt - Number: ");
	monitor_write_dec(regs.int_no);
	monitor_write("\tDetails: ");
	
	if (regs.int_no < 32) {
		int i = regs.int_no;
		if (i > 19) 
			i = 19;
		
		monitor_write(faults[i]);
	} else {
		monitor_write("None");	
	}	
	monitor_put('\n');

	if (interrupt_handlers[regs.int_no] != 0) {
		isr_t handler = interrupt_handlers[regs.int_no];
		handler(regs);
	}
}

// This gets called from our ASM interrupt handler stub.
void irq_handler(registers_t regs)
{
    // Send an EOI (end of interrupt) signal to the PICs.
    // If this interrupt involved the slave.
    if (regs.int_no >= 40)
    {
        // Send reset signal to slave.
        outb(0xA0, 0x20);
    }
    // Send reset signal to master. (As well as slave, if necessary).
    outb(0x20, 0x20);

    if (interrupt_handlers[regs.int_no] != 0)
    {
        isr_t handler = interrupt_handlers[regs.int_no];
        handler(regs);
    }

}

int IRQ_masker(u8int irq_line)
{
	u8int byte;
	u16int port;
	
	/* Invalid IRQ line */
	if (irq_line < 0 || irq_line > 15) 
		return -1;
	
	/* Set PIC device (master / slave) */
	if (irq_line < 8) {
		port = 0x21;
	} else {
		port = 0xA1;
		irq_line -= 8;
	}
	/* Read the internal register IMR and mask the bit */
	byte = inb(port) | (1 << irq_line); 
	outb(port, byte);
	return 0;
}

int IRQ_unmasker(u8int irq_line)
{
	u8int byte;
	u16int port;
	
	/* Invalid IRQ line */
	if (irq_line < 0 || irq_line > 15) 
		return -1;
	
	/* Set PIC device (master / slave) */
	if (irq_line < 8) {
		port = 0x21;
	} else {
		port = 0xA1;
		irq_line -= 8;
	}
	/* Read the internal register IMR and unmask the bit */
	byte = inb(port) & ~(1 << irq_line); 
	outb(port, byte);
	return 0;
}
